#include "data.h"
#include "params.hpp"
#include <qcolor.h>
#include <qlineseries.h>
#ifdef _MSWIN_
#include <windows.h>
#endif

#include "datachart.h"
#include "ticker.h"
#include <QGraphicsScene>
#include <qbrush.h>
#include <qnamespace.h>
#include <sstream>

DataChart::DataChart()
{
  setBackgroundBrush(QBrush(QColor(39, 39, 39)));
  setBackgroundRoundness(7);

  legend()->hide();

  m_seriesHistory = new QLineSeries();
  m_seriesFuture = new QLineSeries();
  m_seriesU1 = new QLineSeries();
  m_seriesU2 = new QLineSeries();
  m_seriesD1 = new QLineSeries();
  m_seriesD2 = new QLineSeries();
  m_mickeyU1 = new QLineSeries();
  m_mickeyU2 = new QLineSeries();
  m_mickeyD1 = new QLineSeries();
  m_mickeyD2 = new QLineSeries();

  m_tm_near = new QLineSeries();
  m_tm_far = new QLineSeries();
  m_extra_dn = new QLineSeries();
  m_extra_up = new QLineSeries();

  addSeries(m_seriesHistory);
  addSeries(m_seriesFuture);
  addSeries(m_seriesU1);
  addSeries(m_seriesU2);
  addSeries(m_seriesD1);
  addSeries(m_seriesD2);
  addSeries(m_mickeyU1);
  addSeries(m_mickeyU2);
  addSeries(m_mickeyD1);
  addSeries(m_mickeyD2);
  addSeries(m_tm_near);
  addSeries(m_tm_far);
  addSeries(m_extra_up);
  addSeries(m_extra_dn);

  for (int i = 0; i < 3; ++i) {
    m_pet_t1[i] = new QLineSeries();
    m_pet_t2[i] = new QLineSeries();
    addSeries(m_pet_t1[i]);
    addSeries(m_pet_t2[i]);
  }

  m_seriesHistory->setColor(Qt::blue);
  m_seriesFuture->setColor(QColor(18, 228, 252));
  m_seriesU1->setColor(QColor(247, 166, 229));
  m_seriesU2->setColor(QColor(247, 166, 229));
  m_seriesD1->setColor(QColor(252, 150, 18));
  m_seriesD2->setColor(QColor(252, 150, 18));
  m_tm_near->setColor(QColor(168, 168, 168));
  m_tm_far->setColor(QColor(168, 168, 168));
  m_mickeyU1->setColor(QColor(89, 159, 245));
  m_mickeyU2->setColor(QColor(89, 159, 245));
  m_mickeyD1->setColor(QColor(159, 245, 89));
  m_mickeyD2->setColor(QColor(159, 245, 89));
  m_extra_up->setColor(QColor(160, 255, 236));
  m_extra_dn->setColor(QColor(255, 236, 160));

  m_pet_t1[0]->setColor(QColor(71, 76, 249, 128));
  m_pet_t2[0]->setColor(QColor(71, 76, 249, 128));
  m_pet_t1[1]->setColor(QColor(236, 181, 51, 128));
  m_pet_t2[1]->setColor(QColor(236, 181, 51, 128));
  m_pet_t1[2]->setColor(QColor(211, 51, 236, 128));
  m_pet_t2[2]->setColor(QColor(211, 51, 236, 128));

  m_axisY = new QValueAxis;
  m_axisX = new QValueAxis;
  m_axisX->setGridLineVisible(false);
  m_axisY->setGridLineVisible(false);

  addAxis(m_axisY, Qt::AlignLeft);
  addAxis(m_axisX, Qt::AlignBottom);
  m_seriesHistory->attachAxis(m_axisY);
  m_seriesFuture->attachAxis(m_axisY);
  m_seriesU1->attachAxis(m_axisY);
  m_seriesU2->attachAxis(m_axisY);
  m_seriesD1->attachAxis(m_axisY);
  m_seriesD2->attachAxis(m_axisY);
  m_mickeyU1->attachAxis(m_axisY);
  m_mickeyU2->attachAxis(m_axisY);
  m_mickeyD1->attachAxis(m_axisY);
  m_mickeyD2->attachAxis(m_axisY);
  m_tm_near->attachAxis(m_axisY);
  m_tm_far->attachAxis(m_axisY);
  m_extra_up->attachAxis(m_axisY);
  m_extra_dn->attachAxis(m_axisY);

  m_seriesHistory->attachAxis(m_axisX);
  m_seriesFuture->attachAxis(m_axisX);
  m_seriesU1->attachAxis(m_axisX);
  m_seriesU2->attachAxis(m_axisX);
  m_seriesD1->attachAxis(m_axisX);
  m_seriesD2->attachAxis(m_axisX);
  m_mickeyU1->attachAxis(m_axisX);
  m_mickeyU2->attachAxis(m_axisX);
  m_mickeyD1->attachAxis(m_axisX);
  m_mickeyD2->attachAxis(m_axisX);
  m_tm_near->attachAxis(m_axisX);
  m_tm_far->attachAxis(m_axisX);
  m_extra_up->attachAxis(m_axisX);
  m_extra_dn->attachAxis(m_axisX);

  m_axisX->setRange(-DATA_LENGTH, FUTURE_LENGTH);

  for (int i = 0; i < 3; ++i) {
    m_pet_t1[i]->attachAxis(m_axisY);
    m_pet_t2[i]->attachAxis(m_axisY);
    m_pet_t1[i]->attachAxis(m_axisX);
    m_pet_t2[i]->attachAxis(m_axisX);
  }

  visibleMickeyLines();
}

void
DataChart::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
  QChart::mousePressEvent(event);
  auto const chartItemPos = mapFromScene(event->scenePos());
  auto const valueGivenSeries = mapToValue(chartItemPos);
  qreal x = valueGivenSeries.x() * g_dp.m_frequence;
  // +m_posIndex;
  // printf("x %.02lf (%.02lfm), y %.02lf\n",
  //        x,
  //        ((x - m_posIndex) / 60),
  //        valueGivenSeries.y());
  // fflush(stdout);

  emit mouseMove(int(x / 60), valueGivenSeries.y());
}

QString
DataChart::getTimerString() const noexcept
{
  std::stringstream ss;
  ss << "Timer: ";
  if (g_dp.t1 || g_dp.t2) {
    auto t1 = g_dp.t1;
    auto t2 = g_dp.t2;
    if (t1 && t2) {
      t1 = std::min(t1, t2);
      t2 = std::max(t1, t2);
    }
    const auto ptr =
      Trade::get_vo_by_index(g_dp.vo, g_dp.vo_size, g_dp.m_posIndex);
    if (ptr) {
      if (t1 > 0)
        ss << int(t1 - ptr->t_now_) / 60 << " m";
      if (t2 > 0) {
        if (t1 > 0)
          ss << ", ";
        ss << int(t2 - ptr->t_now_) / 60 << " m";
      }
    }
  }
  return ss.str().c_str();
}

void
DataChart::setTimerSeries(const int64_t pos) noexcept
{
  m_tm_near->clear();
  m_tm_far->clear();
  if (g_dp.t1) {
    int x1 = g_dp.time_2_x(g_dp.t1);
    m_tm_near->append(x1, maxY_);
    m_tm_near->append(x1, minY_);
  }

  if (g_dp.t2) {
    int x2 = g_dp.time_2_x(g_dp.t2);
    m_tm_far->append(x2, maxY_);
    m_tm_far->append(x2, minY_);
  }
}

void
DataChart::updatePetTimer(QLineSeries* s1, QLineSeries* s2, int pet_id) noexcept
{
  s1->clear();
  s2->clear();

  const time_t t1 = g_pets.pet[pet_id].t1;
  const time_t t2 = g_pets.pet[pet_id].t1;
  const dir_t d = g_pets.pet[pet_id].dir;
  if (d) {
    if (t1) {
      int x = g_dp.time_2_x(t1);
      s1->append(x, maxY_);
      s1->append(x, minY_);
    }

    if (t2) {
      int x = g_dp.time_2_x(t2);
      s2->append(x, maxY_);
      s2->append(x, minY_);
    }
  }
}

void
DataChart::setPetTimer(const int pet_id) noexcept
{
  QLineSeries* s1 = m_pet_t1[pet_id];
  QLineSeries* s2 = m_pet_t2[pet_id];
  updatePetTimer(s1, s2, pet_id);
}

void
DataChart::setMickLines() noexcept
{
  m_mickeyU1->clear();
  m_mickeyU2->clear();
  m_mickeyD1->clear();
  m_mickeyD2->clear();
  m_extra_up->clear();
  m_extra_dn->clear();

  if (g_pets.mickey_lines.nup) {
    m_mickeyU1->append(-float(DATA_LENGTH) / 4, g_pets.mickey_lines.u1);
    m_mickeyU1->append(FUTURE_LENGTH, g_pets.mickey_lines.u1);
    m_mickeyU2->append(-float(DATA_LENGTH) / 4, g_pets.mickey_lines.u2);
    m_mickeyU2->append(FUTURE_LENGTH, g_pets.mickey_lines.u2);
    maxY_ = std::max(maxY_, g_pets.mickey_lines.u2);
  }
  if (g_pets.mickey_lines.ndn) {
    m_mickeyD1->append(-float(DATA_LENGTH) / 4, g_pets.mickey_lines.d1);
    m_mickeyD1->append(FUTURE_LENGTH, g_pets.mickey_lines.d1);
    m_mickeyD2->append(-float(DATA_LENGTH) / 4, g_pets.mickey_lines.d2);
    m_mickeyD2->append(FUTURE_LENGTH, g_pets.mickey_lines.d2);
    minY_ = std::min(minY_, g_pets.mickey_lines.d2);
  }

  if (g_pets.eu > 0) {
    m_extra_up->append(-float(DATA_LENGTH) / 4, g_pets.eu);
    m_extra_up->append(FUTURE_LENGTH, g_pets.eu);
    maxY_ = std::max(maxY_, double(g_pets.eu));
  }

  if (g_pets.ed > 0) {
    m_extra_dn->append(-float(DATA_LENGTH) / 4, g_pets.ed);
    m_extra_dn->append(FUTURE_LENGTH, g_pets.ed);
    minY_ = std::min(minY_, double(g_pets.ed));
  }
}

void
DataChart::setSeries() noexcept
{
  if (!g_dp.tick)
    return;

  if (!m_historyLen || !m_futureLen)
    return;

  int interval = 0;
  if (g_dp.m_frequence == 1)
    interval = 1;
  else
    interval = std::min(((g_dp.m_frequence / 100) + 1) * 3, 12);

  setSeries(m_historyLen, m_futureLen, interval, history, future);

  for (int i = 0; i < 3; ++i) {
    updatePetTimer(m_pet_t1[i], m_pet_t2[i], i);
  }
}

void
DataChart::setSeries(const int historyLen,
                     const int futureLen,
                     const int interval,
                     const Tick* history,
                     const Tick* future) noexcept
{
  resetY();
  m_seriesHistory->clear();
  for (int i = 0; i < historyLen; i += interval) {
    m_seriesHistory->append(i - DATA_LENGTH + 1, history[i].price);
    maxY_ = std::max(maxY_, history[i].price);
    minY_ = std::min(minY_, history[i].price);
  }

  m_seriesFuture->clear();
  if (sos.b_shouw_future) {
    for (int i = 0; i < futureLen; i += interval) {
      m_seriesFuture->append(i + 1, future[i].price);
      maxY_ = std::max(maxY_, future[i].price);
      minY_ = std::min(minY_, future[i].price);
    }
  }

  m_seriesU1->clear();
  m_seriesU2->clear();
  m_seriesD1->clear();
  m_seriesD2->clear();
  if (g_dp.lines.nup) {
    if (g_dp.m_frequence > 1)
      maxY_ = std::max(maxY_, g_dp.lines.u2);
    m_seriesU1->append(-float(DATA_LENGTH) / 4, g_dp.lines.u1);
    m_seriesU1->append(FUTURE_LENGTH, g_dp.lines.u1);
    m_seriesU2->append(-float(DATA_LENGTH) / 4, g_dp.lines.u2);
    m_seriesU2->append(FUTURE_LENGTH, g_dp.lines.u2);
  }
  if (g_dp.lines.ndn) {
    if (g_dp.m_frequence > 1)
      minY_ = std::min(minY_, g_dp.lines.d2);
    m_seriesD1->append(-float(DATA_LENGTH) / 4, g_dp.lines.d1);
    m_seriesD1->append(FUTURE_LENGTH, g_dp.lines.d1);
    m_seriesD2->append(-float(DATA_LENGTH) / 4, g_dp.lines.d2);
    m_seriesD2->append(FUTURE_LENGTH, g_dp.lines.d2);
  }

  if (g_pets.mickey_lines.nup) {
    maxY_ = std::max(maxY_, g_pets.mickey_lines.u2);
  }
  if (g_pets.mickey_lines.ndn) {
    minY_ = std::min(minY_, g_pets.mickey_lines.d2);
  }
  if (g_pets.eu > 0) {
    maxY_ = std::max(maxY_, double(g_pets.eu));
  }
  if (g_pets.ed > 0) {
    minY_ = std::min(minY_, double(g_pets.ed));
  }

  updateY();
}

void
DataChart::resetY()
{
  maxY_ = -1e10;
  minY_ = 1e10;
}

void
DataChart::updateY()
{
  auto h = maxY_ - minY_;
  maxY_ = maxY_ + .05 * h;
  minY_ = minY_ - .05 * h;
  m_axisY->setRange(minY_, maxY_);
}

void
DataChart::visibleTom() noexcept
{
  m_pet_t1[0]->setVisible(sos.b_show_tom);
  m_pet_t2[0]->setVisible(sos.b_show_tom);
}

void
DataChart::visibleJerry() noexcept
{
  m_pet_t1[1]->setVisible(sos.b_show_jerry);
  m_pet_t2[1]->setVisible(sos.b_show_jerry);
}

void
DataChart::visibleMickey() noexcept
{
  m_pet_t1[2]->setVisible(sos.b_show_mickey);
  m_pet_t2[2]->setVisible(sos.b_show_mickey);
}

void
DataChart::visibleMickeyLines() noexcept
{
  if (sos.n_show_space_lines == 0) {
    m_mickeyD1->setVisible(true);
    m_mickeyD2->setVisible(true);
    m_mickeyU1->setVisible(true);
    m_mickeyU2->setVisible(true);
    m_extra_dn->setVisible(true);
    m_extra_up->setVisible(true);

    m_seriesD1->setVisible(false);
    m_seriesD2->setVisible(false);
    m_seriesU1->setVisible(false);
    m_seriesU2->setVisible(false);
  }

  if (sos.n_show_space_lines == 1) {
    m_seriesD1->setVisible(true);
    m_seriesD2->setVisible(true);
    m_seriesU1->setVisible(true);
    m_seriesU2->setVisible(true);

    m_mickeyD1->setVisible(true);
    m_mickeyD2->setVisible(true);
    m_mickeyU1->setVisible(true);
    m_mickeyU2->setVisible(true);
    m_extra_dn->setVisible(true);
    m_extra_up->setVisible(true);
  }

  if (sos.n_show_space_lines == 2) {
    m_mickeyD1->setVisible(false);
    m_mickeyD2->setVisible(false);
    m_mickeyU1->setVisible(false);
    m_mickeyU2->setVisible(false);
    m_extra_dn->setVisible(false);
    m_extra_up->setVisible(false);

    m_seriesD1->setVisible(true);
    m_seriesD2->setVisible(true);
    m_seriesU1->setVisible(true);
    m_seriesU2->setVisible(true);
  }

  if (sos.n_show_space_lines == 3) {
    m_mickeyD1->setVisible(false);
    m_mickeyD2->setVisible(false);
    m_mickeyU1->setVisible(false);
    m_mickeyU2->setVisible(false);
    m_extra_dn->setVisible(true);
    m_extra_up->setVisible(true);

    m_seriesD1->setVisible(false);
    m_seriesD2->setVisible(false);
    m_seriesU1->setVisible(false);
    m_seriesU2->setVisible(false);
  }

  if (sos.n_show_space_lines == 4) {
    m_mickeyD1->setVisible(true);
    m_mickeyD2->setVisible(true);
    m_mickeyU1->setVisible(true);
    m_mickeyU2->setVisible(true);
    m_extra_dn->setVisible(false);
    m_extra_up->setVisible(false);

    m_seriesD1->setVisible(false);
    m_seriesD2->setVisible(false);
    m_seriesU1->setVisible(false);
    m_seriesU2->setVisible(false);
  }
}

int
DataChart::sampler_data() noexcept
{
  if (!g_dp.tick)
    return (-1);

  m_historyLen = down_sampler(g_dp.tick,
                              g_dp.tick_size,
                              g_dp.m_frequence,
                              g_dp.m_posIndex,
                              history,
                              DATA_LENGTH,
                              -1);
  m_futureLen = down_sampler(g_dp.tick,
                             g_dp.tick_size,
                             g_dp.m_frequence,
                             g_dp.m_posIndex,
                             future,
                             FUTURE_LENGTH,
                             1);

  return (0);
}

void
DataChart::draw() noexcept
{
  sampler_data();

  if (m_historyLen > 0 && m_futureLen > 0) {
    // && m_rawHistoryLen > 0 && m_rawFutureLen > 0)

    if (!set_params_data()) {
      setSeries();
    }
  }
  update();
}
